﻿/////////////////////////////////////////////////////////////////////////////////////////////
// <copyright file="SessionRunspaceSingleton.cs" company="Microsoft Corporation">
//     Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
/////////////////////////////////////////////////////////////////////////////////////////////

namespace Microsoft.LiveAtEdu.Samples.Powershell
{
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Linq;
    using System.Management.Automation;
    using System.Management.Automation.Runspaces;
    using System.Security;
    using System.Text;

    /// <summary>
    /// Responsible for ensuring that only one instance of session per session type
    /// is created in a process.
    /// </summary>
    /// <remarks>
    /// This is mainly used from the LiveAtEduDemo project. If needed, there could 
    /// be multiple sessionrunspace created in a process through directly calling 
    /// the PSSessionRunspaceFactory.
    /// </remarks>
    public static class SessionRunspaceSingleton
    {
        /// <summary>
        /// Default script run to check validity of session.
        /// </summary>
        private const string DefaultScript = "Get-OwaMailboxPolicy";

        /// <summary>
        /// SessionRunspace instance for OnPremise.
        /// </summary>
        private static SessionRunspace onPremiseInstance;

        /// <summary>
        /// SessionRunspace instance for OutlookLive.
        /// </summary>
        private static SessionRunspace outlookLiveInstance;

        /// <summary>
        /// Get the unique instance of SessoinRunsapce based on the session type.
        /// </summary>
        /// <param name="sessionType">Type of session.</param>
        /// <param name="userName">The user name to authenticate the session.</param>
        /// <param name="password">The password to authenticate the session.</param>
        /// <param name="connectionUri">Uri for creating the connection.</param>
        /// <returns>Singleton instance of SessoinRunsapce.</returns>
        /// <remarks>
        /// If creation fails, then null is returned.
        /// </remarks>
        public static SessionRunspace GetInstance(SessionType sessionType, string userName, SecureString password, string connectionUri)
        {
            // Validate the inputs (Throw ArgumentOutOfRangeException for all invalid inputs).
            if (string.IsNullOrEmpty(userName))
            {
                throw new ArgumentOutOfRangeException("userName");
            }

            if (password == null)
            {
                throw new ArgumentOutOfRangeException("password");
            }

            if (string.IsNullOrEmpty(connectionUri))
            {
                throw new ArgumentOutOfRangeException("connectionUri");
            }

            switch (sessionType)
            {
                case SessionType.OnPremiseSession:
                    if (onPremiseInstance == null || !IsSessionValid(onPremiseInstance))
                    {
                        onPremiseInstance = SessionRunspaceFactory.CreateInstance(sessionType, userName, password, connectionUri);
                    }

                    return onPremiseInstance;

                case SessionType.OutlookLiveSession:
                    if (outlookLiveInstance == null || !IsSessionValid(outlookLiveInstance))
                    {
                        outlookLiveInstance = SessionRunspaceFactory.CreateInstance(sessionType, userName, password, connectionUri);
                    }

                    return outlookLiveInstance;

                default:
                    return null;
            }
        }

        /// <summary>
        /// Close all the created session runsapce instances.
        /// </summary>
        public static void CloseInstance()
        {
            if (onPremiseInstance != null)
            {
                onPremiseInstance.CloseInstance();
                onPremiseInstance = null;
            }

            if (outlookLiveInstance != null)
            {
                outlookLiveInstance.CloseInstance();
                outlookLiveInstance = null;
            }
        }

        /// <summary>
        /// Check if session is still valid.
        /// </summary>
        /// <returns>True if session is valid, else False.</returns>
        /// <param name="sessionRunspace">Instance of Session Runspace.</param>
        /// <remarks>
        /// Since session is created only once, as per this class design, it could become invalid.
        /// Here we run a simple command and make sure that the session is still valid.
        /// </remarks>
        private static bool IsSessionValid(SessionRunspace sessionRunspace)
        {
            try
            {
                // Try to perform any simple operation to see if the session is valid
                // In this case we are doing a get Get-OwaMailboxPolicy
                var command = new PSCommand();
                command.AddScript(DefaultScript);
                var results = sessionRunspace.ExecuteCommand(command);

                if (results != null && results.Count > 0)
                {
                    return true;
                }
            }
            catch
            {
                // Ignore exception.
            }

            return false;
        }
    }
}
